* May or may not be covered by US ITAR encryption export regulations, if
* in doubt, don't export it. It would be pretty stupid if it was, but
* hey, governments have done some pretty stupid things before now....
*
* This program is copyright Henry Hastur 1994, but may be freely distributed,
* modified, incorporated into other programs and used, as long as the
* copyright stays attached and you obey any relevant import or export
* restrictions on the code. No warranty is offered, and no responsibility
* is taken for any damage use of this program may cause. In other words,
* do what you want with it, but don't expect me to pay up if anything
* unexpected goes wrong - you're using it at your own risk...
*
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#ifdef __SASC
#undef getc
#undef putchar
#endif
/* Few definitions from PGP for it's header/algorithm versions */
#define CURRENT_VERSION 0x02
#define RSA_VERSION 0x01
#define ID_SIZE 8
/* define TRUE and FALSE */
#define TRUE 1
#define FALSE 0
/* A byte */
#ifndef __SASC
typedef unsigned char byte;
#endif
/* Few global variables */
static int verbose = FALSE;
static int conventional = FALSE;
static int adding = FALSE;
static char file_name [1024];
static int file_open = FALSE;
static FILE *afp;
/* int_handler() - tidy up and exit */
static void int_handler(int unused)
{
long fpos;
int c;
/* If we've still got a file open */
if (file_open) {
/* If we can still write to it, erase it */
if (afp) {
fseek (afp, 0l, 2);
fpos = ftell (afp);
fseek (afp, 0l, 0);
while (fpos --)
putc (0, afp);
}
#ifndef UNIX
/* Finally unlink it */
unlink (file_name);
#endif
}
exit (1);
}
/* Read a PGP ctb-lengh */
static long read_length (int c,
FILE *fp)
{
static int bytes [] = { 1, 2, 4, 0 };
long len = 0;
int n;
n = bytes [c & 0x03];
if (!n)
return 0x7FFFFFFF;
for (; n > 0 ; n--) {
len *= 256;
len += getc(fp);
}
return len;
}
/* Write a PGP ctb-length */
static void write_length (int ctb,
unsigned long length,
FILE *fp)
{
unsigned long mask;
int bytes, shift,c;
ctb &= 0xFC;
if (length < 256) {
mask = 0xFF;
bytes = 1;
shift = 0;
}
else if (length < 65536) {
mask = 0xFF00;
bytes = 2;
shift = 8;
ctb |= 1;
}
else {
mask = 0xFF000000;
shift = 24;
bytes = 4;
ctb |= 2;
}
putc (ctb, fp);
while (bytes-- > 0) {
c = ((length & mask) >> shift);
mask >>= 8;
shift -= 8;
putc (c, fp);
}
}
/* Hunt through pubring.pgp for the appropriate secret key */
#define ID_FROM_NAME 0
#define LENGTH_FROM_ID 1
static int find_key_id(byte *id,
int *length,
char *s,
int type)
{
char *path;
char pub_name [1024];
FILE *pub = NULL;
int c1, c2, len, i, klen, c;
char userid [256];
/* Find pubring.pgp */
if (path = getenv("PGPPATH")) {
sprintf (pub_name, "%s/pubring.pgp", path);
pub = fopen(pub_name, "rb");
}
if (!pub)
pub = fopen ("pubring.pgp", "rb");
if (!pub) {
fprintf (stderr,"Can't find pubring.pgp in $PGPPATH or . : exiting !\n");
exit (1);
}
/* Read the contents till we find what we're looking for */
while ((c1 = getc(pub)) != EOF) {
c2 = (c1 & 0xBC);
switch (c2) {
/* Secret key, probably revocation cert. */
case 0x94:
len = read_length (c1, pub);
for (; len > 0; len--)
(void) getc (pub);
break;
/* Public key - grab id and length */
case 0x98:
len = read_length (c1, pub);
for (i = 0; i < 8; i++)
(void) getc (pub);
len -= 10;
/* OK, here we are at the public modulus, get the
size from the MPI header */
klen = getc(pub) * 256;
klen += getc (pub);
/* Return the length for the caller to use */
*length = klen;
/* We only need the last 64 bits */
len -= ((klen + 7) / 8);
i = (((klen + 7) / 8) - ID_SIZE);
/* Skip unneccesary bytes */
for (; i > 0; i--) {
(void) getc (pub);
}
if (type == ID_FROM_NAME) {
for (i = 0; i < ID_SIZE; i++) {
id [i] = getc(pub);
}
}
else {
/* Looking for length from ID */
int found_id = TRUE;
for (i = 0; i < ID_SIZE; i++) {
if (id[i] != getc(pub))
found_id = FALSE;
}
if (found_id) {
fclose (pub);
return TRUE;
}
}
for (; len > 0 ; len--) {
(void) getc (pub);
}
break;
/* Keyring trust, comment */
case 0xB0:
case 0xB8:
len = getc (pub);
for (; len > 0; len--)
(void) getc (pub);
break;
/* USER ID ! */
case 0xB4:
len = getc (pub);
if (type == ID_FROM_NAME) {
for (i = 0; i < len; i++) {
c = getc (pub);
userid [i] = tolower (c);
}
userid [i] = 0;
if (strstr(userid, s)) {
if (verbose)
fprintf (stderr, "Found user: %s\n",userid);
return TRUE;
}
}
else
for (i = 0; i < len; i++)
(void) getc (pub);
break;
/* Anything we don't care about */
default:
len = read_length (c1, pub);
for (; len > 0; len--)
(void) getc (pub);
break;
}
}
fclose (pub);
/* Uh-oh, failed to find it ! */
return FALSE;
}
/* Strip_headers() : Should be obvious what this does, really ! */
static void strip_headers(FILE *fp)
{
int c1,c2;
long len;
int i;
byte id [ID_SIZE];
int key_length;
byte key_length_found = FALSE;
byte rsa_written = FALSE;
int mpi_length;
/* Run through the whole message checking each packet */
while ((c1 = getc(fp)) != EOF) {
c2 = (c1 & 0xBC);
switch (c2) {
/* Public key encoded packet */
case 0x84:
/* Read length */
len = read_length(c1, fp);
if (verbose)
fprintf (stderr, "Found %d byte RSA packet.\n",
len);
/*
We only support ONE RSA block ! This is because
we have no idea of the file format when we start
adding headers, so we have to assume that this
is the case. Warn the user, then abort...
*/
if (rsa_written) {
fprintf (stderr, "WARNING: More than one RSA block found... stripping extra block !\n");
/* Throw away the block */
ohno_abort_abort:
while (len-- > 0)
(void) getc (fp);
break;
}
/* Check for conventional encryption specified */
if (conventional) {
fprintf (stderr, "WARNING: You specified conventional encryption with an RSA-encrypted file !\nI hope you know what you're doing... stripping RSA header....\n");
goto ohno_abort_abort;
}
/* Check public key version byte */
c1 = getc (fp);
if (c1 != CURRENT_VERSION) {
fprintf(stderr, "Hmm, PK version %d not %d, may not decrypt at recipient\n", c1, CURRENT_VERSION);
}
/* Strip key ID */
for (i = 0; i < ID_SIZE; i++)
id[i] = getc (fp);
if (find_key_id (id,&key_length,NULL,LENGTH_FROM_ID)) {
key_length_found = TRUE;
}
/* Check RSA version byte */
c1 = getc(fp);
if (c1 != RSA_VERSION) {
fprintf (stderr, "Hmm, RSA version %d not %d, may not decrypt at recipient\n", c1, RSA_VERSION);
}
/* Strip MPI prefix */
mpi_length = getc(fp);
mpi_length = mpi_length * 256 + getc(fp);
/* Now, we have a problem in that PGP may generate
an RSA block shorter than your key, in which
case decryption is likely to fail. Check for
this and warn the user ! */
if (!key_length_found) {
fprintf (stderr, "Hmm, couldn't get the length of this key, so can't verify that decryption\nwill be successful.\n");
}
else {
if (((mpi_length + 7) / 8) !=
((key_length + 7) / 8)) {
fprintf (stderr, "WARNING : Short RSA block output, decryption will probably fail if used !\n");
}
}
/* Copy remaining data from packet */
len -= 12;
for (; len > 0; len--)
putchar (getc(fp));
rsa_written = TRUE;
break;
/* IDEA packet */
case 0xA4:
/* Read length */
len = read_length(c1, fp);
if (verbose)
fprintf (stderr, "Found %d byte IDEA packet.\n",
len);
/* Copy data from packet */
for (; len > 0; len--)
putchar (getc (fp));
break;
default:
/* Oh no ! Don't know what this is - just skip it ! */